// ----------------------------------------------------------------------------
// stl_unit_test.cpp
// ----------------------------------------------------------------------------
#include <fw/base/core/macros.h>
#if defined(FW_ENABLE_UNIT_TEST)
#include <fw/base/core/debugconsole.h>
#include <gtest/gtest.h>
#include <fw/base/runtime/commandline.h>
#include <fw/base/runtime/hash.h>
#include <fw/base/runtime/radix_sort.h>
#include <fw/base/runtime/seed_seq.h>
#include <fw/base/runtime/allocator.h>
#include <fw/base/stl/stl_string.h>
#include <fw/base/stl/stl_array.h>

#include <random>

TEST(runtime, string_proxy)
{
	ASSERT_EQ(FW_STRICMP("aBcDeFg", "AbCdEfG"), 0);
	ASSERT_NE(FW_STRICMP("aBcDeFg", "aBcDeF" ), 0);
}

TEST(runtime, CommandLine)
{
	//--------
	// test0
	int			test0_argc = 0;
	const char* test0_argv[] = {0, };
	fw::CommandLine cmdLine0(test0_argc, test0_argv);
	
	ASSERT_EQ(cmdLine0.HasArg ("aaa"), false);
	ASSERT_EQ(cmdLine0.GetFlag("aaa"), false);
	ASSERT_EQ(cmdLine0.FindOption("bbb"), nullptr);
	
	//--------
	// test1
	int			test1_argc = 9;
	const char* test1_argv[] =
	{
		"test.exe",
		"-aaa", "true",
		"-bbb", "hoge",
		"-ccc", "123",
		"-ddd", "3.141592",
	};
	fw::CommandLine cmdLine1(test1_argc, test1_argv);
	
	ASSERT_EQ(cmdLine1.HasArg ("aaa"), true);
	ASSERT_EQ(cmdLine1.GetFlag("aaa"), true);
	ASSERT_EQ(strcmp(cmdLine1.FindOption("bbb"), "hoge"), 0);
	ASSERT_EQ(cmdLine1.GetInt  ("ccc"), 123);
	ASSERT_EQ(cmdLine1.GetFloat("ddd"), 3.141592f);
	ASSERT_EQ(cmdLine1.GetFlag ("eee"), false);
	ASSERT_EQ(cmdLine1.GetInt  ("eee"), 0);
	ASSERT_EQ(cmdLine1.GetFloat("eee"), 0.0f);
	
	//--------
	// バグリストからの対応で追加したテストはここから下に書く.
	
}

TEST(runtime, HashValue32)
{
	// 文字列のハッシュ値.
	const char* hashName0 = "hashName";
	stl::string hashName1  ("hashName");
	
	uint32_t hashValue0 = fw::HashValue32(hashName0);
	uint32_t hashValue1 = fw::HashValue32(hashName1);
	
	ASSERT_EQ(hashValue0, hashValue1);
	
	// 任意のバイナリ列のハッシュ値.
	const char binaryData0[] = { 0, 0, 0, 0, 0 };
	const char binaryData1[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
	
	uint32_t binaryHash0 = fw::HashValue32Array(&binaryData0, sizeof(binaryData0)/sizeof(char), 0);
	uint32_t binaryHash1 = fw::HashValue32Array(&binaryData1, sizeof(binaryData1)/sizeof(char), 0);
	
	ASSERT_NE(binaryHash0, binaryHash1);
	
	// 任意の構造体のハッシュ値.
	struct HashStruct { uint32_t a, b; };
	HashStruct h0 = { 123, 456 };
	HashStruct h1 = { 456, 789 };
	
	uint32_t structHash0 = fw::HashValue32(h0);
	uint32_t structHash1 = fw::HashValue32(h1);
	
	ASSERT_NE(structHash0, structHash1);
	
}

TEST(runtime, HashValue64)
{
#define TEST_STR "hogea"
    const char* hoge = TEST_STR;
    constexpr uint64_t value64 = fw::murmur::StaticHashValueInternal64(TEST_STR, sizeof(TEST_STR), 0);
    constexpr uint32_t value32 = fw::murmur::StaticHashValueInternal32(TEST_STR, sizeof(TEST_STR), 0);
    
    FW_PRINTF("*************** hoge64[0x%llx]->[0x%llx]\n", fw::HashValue64(hoge), value64);
    FW_PRINTF("*************** hoge32[0x%lx]->[0x%lx]\n", fw::HashValue32(hoge), value32);
    
}

TEST(runtime, MemoryAllocator)
{
    FW_REGISTER_OBJECT_INITIALIZE();

    {
		char* memoryBlock = reinterpret_cast<char*>(FW_MALLOC(sizeof(char) * 10));
		if(memoryBlock)
		{
			FW_PRINTF("Allocate memory...");
			FW_FREE(memoryBlock);
			FW_PRINTF("Deallocate memory...\n");
			
			char* leakMemory = reinterpret_cast<char*>(FW_MALLOC(sizeof(char) * 16));
			FW_UNUSED(leakMemory);
            
            
		}
	}
    
    FW_REGISTER_OBJECT_DESTROY();
}

namespace
{
    struct Value { int v; Value():v(0){} };
    
    template<typename T> void RADIX_SORT_TEST()
    {
        static const size_t kArraySize = 8192;
        typedef T RadixKey;
        
        std::seed_seq       seedSeq1 = { 1, 2, 3, 4, 5, 6, }; // TODO : より軽量なseed_seqの実装.
        fw::seed_array<6>   seedSeq2 = { 1, 2, 3, 4, 5, 6, };
        
        std::mt19937 engine1(seedSeq1);
        std::mt19937 engine2(seedSeq2);
        
        ASSERT_EQ(engine1, engine2);
        
        std::uniform_int_distribution<RadixKey> distribution(1, 4096);
        std::array<RadixKey, kArraySize> keyArray;
        std::array<RadixKey, kArraySize> keyArrayT;
        std::array<Value,    kArraySize> valueArray;
        std::array<Value,    kArraySize> valueArrayT;
        
        for(size_t i=0; i<kArraySize; ++i)
        {
            keyArray[i]     = distribution(engine2);
            valueArray[i].v = static_cast<int>(i);
        }
        
        fw::RadixSort<RadixKey, Value>(&keyArray[0], &keyArrayT[0], &valueArray[0], &valueArrayT[0], kArraySize);
        
        for(size_t i=0; i<(kArraySize-1); ++i)
        {
            ASSERT_LE(keyArray[i], keyArray[i+1]);
        }
    }
    
} // unnamed namespace

TEST(runtime, RadixSort)
{
    RADIX_SORT_TEST<uint32_t>();
    RADIX_SORT_TEST<uint64_t>();
}

#endif // FW_ENABLE_UNIT_TEST

void RUNTIME_UNIT_TEST()
{
}